set文羅列とdelete文羅列を入力すると、deleteを適用したset文羅列にできる差分#8
set文羅列とdelete文羅列を入力すると、deleteを適用したset文羅列にできる差分#8shogosanz wants to merge 5 commits intocodeout:masterfrom
Conversation
|
パッチありがとうございます!
同じ問題意識を持っています。が、その問題は少々大きいため、junoser では「コンフィグの生成には関与しない。パースするだけ」にフォーカスしています。
圧縮された最適なコンフィグを出力するのは 既存の機能にはないものですので、 いずれにせよ、いい名前を思いつく必要はありそうですよね。 入出力パターン
今回 4 に特化しています。 順序によって出力が変わるSyntax パターン
今回 1 に特化しています。 今回実装いただいたものにいい名前をつけられるのであれば、これは便利そうかなと思いました。 |
|
レビューありがとうございます。
なるほど。像を理解しました。
名前に関しても気持ち悪いところはあり、-d は既にあるのに、 --deleteとか名付けちゃってるとか 他にも、例えば や や ・ ↓ で処理できる、万能なコマンドがあっても良いと考えています。(compactとかoptimとか) ただ、deactivate,activateを除くいずれのsyntaxパターンにおいても、set&deleteだけで表現可能だという認識なので、deleteコマンドのみ独立性があっても良いかなとも考えています。 rename,copy,insert・・・などのコマンドの実装についてこの差分では、構造化configを、rubyとして扱いやすくkey参照を可能にする順序性データ構造であるhashにしていますので、これによって他のコマンドの実装を容易にすると考えています。 パターンについて入出力パターンの1,2,3,4にそれぞれ対応するのは数行追加するだけで行けそうですが(そもそも、このような場合についてはユーザーがパイプで渡せば良いと考えていました)、順序によって出力が変わるsyntaxパターンについてはさらなる検討が必要ですね(特に、この実装だとdeactivate+activate関連が考慮されていませんでした・・・これは認識不足でした。一時的に退避させ、生成後に追加するような構造にしようかと思います) 特に、delete文set文間においても適用順序で出力が異なる件については、「set文を既存のConfig、delete文は今から注入する」というベースで考えていたので、「いかなる順序だろうとset文の集合に対してdelete文を一方的に適用すれば良い」としてきましたが、この点は要修正としたいと思います。(順序に意味が生じるように。) これらは明確に区別しようと考えています。 疑問点一般論として、手順書を生成する前にどのような作業を行っているのかというのがいまいち把握出来ておりません。 しかし、多くの人にとって使いやすく有るべきだと考えるので、この点については、より汎用的な設計にしたいと考えます。 まとめ 追加したい・修正したい点
将来的に追加したい機能
|
|
名前はさておき、
万能コマンドを用意しておいて「いまは この場合やりたいのは「現状のコンフィグと変化を与えるコマンド( あとで内部実装も拝見してコメントしますね。 |
なるほど。
仰る通りです。
ありがとうございます! 宜しくお願い致します。
十分なdelete文の例 これは終了されずに、実行が続けられるべきだと考えますので、要修正とします。 |
|
いくつか修正と追加を致しました。
junoser-compareコマンドに関わる差分
例Config1 Config2 結果 hash_cmp関数hashを比較し、差を実現するset文あるいはdelete文の、もととなるhashを返します。 including関数Configの包含関係を調べます。あるConfigがあるConfigの一部であるか否かをhashを用いて判定しています。 |
|
いろいろ検討いただいているのに遅くてすみません。 内部実装のところ、一点コメント差し上げたく。 おさらい: delete の適用がむずかしい理由
この一点かなと思っています。具体的には で 問題: パフォーマンス現在速いとは言い難いので、なるべくパースする部分を減らしたいところ。 提案: 次のような戦略ではいかがでしょうか?
ヒント: パースした段階の構文木を取ってくるのは可能require 'junoser'
parser = Junoser::Parser.new
pp parser.parse('set interfaces em0 unit 0 family inet address 1.1.1.1/32')
|
|
実装例: https://gist.github.com/codeout/a5a47e4143c2473767a509d25ddcb036 のように、消す対象そのものがなかったらどうふるまうべきか、など検討は若干あまいかもです |
|
おお! ありがとうございます!
これは夢が広がりました!良いヒントをありがとうございます。
なるほど、これは良い案だと思います。 以下細かい点について述べます。 例えば、頂いた実装例と共に以下のような例を考えます。 例まず、実装例有難うございます。 ↓出力 となってしまいます。 この本質は、transformからのpatternの生成にあります。 最後の子が消えてしまっているところに、情報の欠落があります。 改善点以下のような実装はどうでしょう。 例 まず、 によって、delete文に構文的な意味を与えます。 うまいこと変換し、 そして、これを.sub!(/#{pattern}/) { $1 }します。 全てのset文に対して適用が終わると、以下のようになるはずです。 その後、ある文の部分になるようなものは消します。(set文は長いほうが偉い) 以上のようなアルゴリズムはどうでしょうか。 junoser-compareについてjunoser-compare に関しては、別PRに致します。 名前に関して
とてもしっくりきます! |
|
なるほど! いいですね。
は最終行だけでいい。 これ愚直にやると |
は、 https://gist.github.com/codeout/a5a47e4143c2473767a509d25ddcb036 更新しました |
悩み: squash はソートを含むかなら1行にしていいと思いますが、 を1行にしていいかは迷いますね。 a行までの時点ではその行に意味がありますので。 squash を「全て入れた後の設定をsquash」とするか「1行ずつ入れて行って、効果のない行だけ削る」とするかによりそうです。 趣旨としては前者に近く、であればソートしちゃったほうが文字列処理がラクそうです。 |

大変僭越ながらJunoserの機能を実装したので、ご査収いただければ幸いです。
使い方
junoser --delete CONFIG_FILE概要
Delete文は既存のConfigによって適用のされ方が異なるという性質があります。
これは、手順書の自動作成においてこれを判断をするために人間の手が介入させなければならないというデメリットを生じさせてしまうものです。
例:
Junoserは「機器にログインせずに作業が出来るようにする」という目的を元に作られていると認識しています。この機能は、この目的に合致するのではないかと考えています。
もしこの差分がマージされた場合、以下のようなメリットが考えられます。
実装
大まかなフローは以下になります。
関数
apply
引数: io_or_string
戻り値: delete文適用後のset文羅列
set文とdelete文を分解し、いくつかの関数を呼び出し、処理する、メインのフローです。
struct_to_hash
引数: struct
戻り値: hash
ConfigのStructureをHashに変換します。
後々Delete文を適用しやすくするためのものです。
内部では、struct_to_first_hashとhash_value_to_hashを用いています。
例
↓
hash_value_to_hash
引数: hash
戻り値: hash
入力されたHashのValueたちをさらにstruct_to_first_hashでHash化していく再帰関数です。
struct_to_first_hash
引数: struct
戻り値: hash
一番外側にある構造だけをHashにし、内側はすべて文字列として扱ってしまうものです。
例
↓
apply_delete
引数: set文のhash , delete文のhash
戻り値: 適用後のset文のhash
delete文は一行ずつ適用されるため、ret_first_key_and_value関数を用いて、hashの一番最初のKeyとValueを取得してくるものです。これによって、Set文のKeyと比較し、正しく削除します。
hash_to_struct
引数: hash
戻り値: struct
最後にhashをStructに直すためのものです。
hash_to_struct_iterでジェネレートされるStringを追加していくだけのものです。
例
↓
hash_to_struct_iter
引数: hash
戻り値: string(structの部分)
Hashを少しずつStringになおしていき、最終的にStructureにします。
これを実現するためのジェネレータとなる再帰関数です。
ret_first_key_and_value
引数: hash
戻り値: key,value
hashの一番最初のKeyとValueを取得します。Delete文は一文ずつ用いるので、最初だけで全てを取得したことになります。